package main

import (
	"bytes"
	"fmt"
	"runtime"
	"strconv"
	"sync"
	"sync/atomic"
)

// ReentrantLock 重入锁
type ReentrantLock struct {
	sync.Mutex
	recursion int32 // 这个G重入的次数
	owner     int64 // 当前持有锁的G id
}

// GoroutineID 获取当前G的ID
func GoroutineID() int64 {
	var buf [64]byte
	var s = buf[:runtime.Stack(buf[:], false)]
	s = s[len("goroutine "):]
	s = s[:bytes.IndexByte(s, ' ')]
	gid, _ := strconv.ParseInt(string(s), 10, 64)
	return gid
}

// RenntrantMutex 包装一个Mutex，支持可重入
type RenntrantMutex struct {
	sync.Mutex
	recursion int32 // 这个G重入的次数
	owner     int64 // 当前持有锁的G id
}

func (m *RenntrantMutex) Lock() {
	gid := GoroutineID()
	// 如果是同一个goroutine
	if atomic.LoadInt64(&m.owner) == gid {
		m.recursion++
		return
	}

	m.Mutex.Lock()
	// 当前Goroutine第一次获取到锁，记录下当前goroutine的ID，调用次数+1
	atomic.StoreInt64(&m.owner, gid)
	m.recursion = 1
}

func (m *RenntrantMutex) UnLock() {
	gid := GoroutineID()
	// 非持有锁的goroutine释放锁，则panic
	if atomic.LoadInt64(&m.owner) != gid {
		panic(fmt.Sprintf("wrong the owner(%d): %d", m.owner, gid))
	}
	// 调用次数-1
	m.recursion --
	if m.recursion != 0 { // 如果这个goroutine没有完全释放，直接返回
		return
	}
	// 此goroutine最后一次调用，释放锁
	atomic.StoreInt64(&m.owner,-1)
	m.Mutex.Unlock()
}

func main() {
	var mutex = &RenntrantMutex{}
	mutex.Lock()
	mutex.Lock()
	fmt.Println(111)
	mutex.UnLock()
	mutex.UnLock()

}
